package org.hope.ontology;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.hope.nlp.SearchTerm;
import org.hope.ontology.owlApi.OntologyManager;
import org.hope.result.City;
import org.hope.result.Hotel;
import org.hope.result.RefineCriteria;
import org.hope.result.Result;
import org.hope.util.CodeValuePair;
import org.hope.util.Constants;
import org.hope.util.HopeLogger;
import org.semanticweb.owlapi.model.OWLClassExpression;
import org.semanticweb.owlapi.model.OWLNamedIndividual;
import org.semanticweb.owlapi.model.OWLNamedObject;

public class OntologySearchCordinator
{
	String isNearPlaceOf = "http://www.travel.net/ontologies/travel.owl#placeIsNearPlace";
	String isInLocation = "http://www.travel.net/ontologies/travel.owl#placeIsInLocation";
	String isNearLocation = "http://www.travel.net/ontologies/travel.owl#placeIsNearLocation";

	String isContainsPlace = "http://www.travel.net/ontologies/travel.owl#locationContainsPlace";

	String isInTown = "http://www.travel.net/ontologies/travel.owl#isInTown";
	String isInCity = "http://www.travel.net/ontologies/travel.owl#isInCity";
	String isInSuburb = "http://www.travel.net/ontologies/travel.owl#isInSuburb";
	String isInRegion = "http://www.travel.net/ontologies/travel.owl#isInRegion";
	String isInDistrict = "http://www.travel.net/ontologies/travel.owl#isInDistrict";

	private OntologyManager ontologyManager;

	public void init() throws Exception
	{
		ontologyManager = OntologyManagerFactory.getOntologyManager( OntologyManagerFactory.OWL_API );
		ontologyManager.init();
	}

	public List<Result> searchOntology( List<SearchTerm> searchTerms )
	{
		List<Result> results = new ArrayList<Result>();

		HopeLogger.log( "----------- Get ontology terms -----------" );
		List<SearchTerm> ontoTerms = ontologyManager.getOntologyTerms( searchTerms );

		int refineFilterNo = 0;

		// set to keep the locations
		Map<String, City> locationCodeMap = new HashMap<String, City>();
		Map<String, RefineCriteria> criteriaMap = new HashMap<String, RefineCriteria>();

		Set<OWLClassExpression> allTermExpressions = new HashSet<OWLClassExpression>();
		// here we have to create rules from search terms
		for ( SearchTerm searchTerm : ontoTerms )
		{
			List<OntoTerm> owlClasses = searchTerm._getOwlClasses();

			Set<OWLClassExpression> classEx = new HashSet<OWLClassExpression>();

			// if there are classes, we create rules from them
			if ( !owlClasses.isEmpty() )
			{
				for ( OntoTerm ontoTerm : owlClasses )
				{
					classEx.add( ontologyManager.getOWLClass( ontoTerm.getIri() ) );

					RefineCriteria refineCriteria = new RefineCriteria();
					String refineQuestion = ontologyManager.getAnnotationPropertyValueString( ontologyManager.getNamedIndividual( ontoTerm.getIri() ), ontologyManager.getOWLAnnotationProperty( OntologyManager.strApRefineQuestion ) );
					if ( refineQuestion != null )
					{
						if ( !criteriaMap.containsKey( refineQuestion ) )
						{
							refineCriteria.setQuestion( refineQuestion );
							refineCriteria.setCode( Constants.FILTER + Constants.FILTER_SEPERATOR + refineFilterNo );
							ArrayList<OWLNamedObject> refineOptions = ontologyManager.getAnnotationPropertyValueObjects( ontologyManager.getOWLClass( ontoTerm.getIri() ), ontologyManager.getOWLAnnotationProperty( OntologyManager.strApRefineOption ) );
							if ( refineOptions != null )
							{
								for ( OWLNamedObject owlNamedObject : refineOptions )
								{
									CodeValuePair<String, String> codeValuePair = new CodeValuePair<String, String>();
									String option = ontologyManager.getAnnotationPropertyValueString( ontologyManager.getNamedIndividual( owlNamedObject.getIRI().toString() ), ontologyManager.getOWLAnnotationProperty( OntologyManager.strApDisplayName ) );
									String optionCode = owlNamedObject.getIRI().getFragment();

									codeValuePair.setValue( option );
									codeValuePair.setCode( optionCode );

									refineCriteria.getOptions().add( codeValuePair );
								}

								criteriaMap.put( refineQuestion, refineCriteria );
							}
						}
					}

				}
				// allTermExpressions.add( ontologyManager.getOWLObjectUnionOf( classEx ) );
			}

			List<OntoTerm> owlIndividuals = searchTerm._getOwlIndividuals();
			if ( !owlIndividuals.isEmpty() )
			{
				// Set<OWLClassExpression> classEx = new HashSet<OWLClassExpression>();
				for ( OntoTerm ontoTerm : owlIndividuals )
				{
					// if individual is a location
					if ( isLocation( ontoTerm ) )
					{
						if ( isCity( ontoTerm ) )
						{
							classEx.add( ontologyManager.getOWLObjectHasValue( ontologyManager.getObjectProperty( isInCity ), ontologyManager.getNamedIndividual( ontoTerm.getIri() ) ) );
						}
						else if ( isSuburb( ontoTerm ) )
						{
							classEx.add( ontologyManager.getOWLObjectHasValue( ontologyManager.getObjectProperty( isInSuburb ), ontologyManager.getNamedIndividual( ontoTerm.getIri() ) ) );
						}
						else if ( isRegion( ontoTerm ) )
						{
							classEx.add( ontologyManager.getOWLObjectHasValue( ontologyManager.getObjectProperty( isInRegion ), ontologyManager.getNamedIndividual( ontoTerm.getIri() ) ) );
						}
						else
						{
							classEx.add( ontologyManager.getOWLObjectHasValue( ontologyManager.getObjectProperty( isNearLocation ), ontologyManager.getNamedIndividual( ontoTerm.getIri() ) ) );
							classEx.add( ontologyManager.getOWLObjectHasValue( ontologyManager.getObjectProperty( isInLocation ), ontologyManager.getNamedIndividual( ontoTerm.getIri() ) ) );
						}

						// when it is a location, add the location code
						String locationCode = ontologyManager.getAnnotationPropertyValueString( ontologyManager.getNamedIndividual( ontoTerm.getIri() ), ontologyManager.getOWLAnnotationProperty( OntologyManager.code ) );
						String locationName = ontologyManager.getAnnotationPropertyValueString( ontologyManager.getNamedIndividual( ontoTerm.getIri() ), ontologyManager.getOWLAnnotationProperty( OntologyManager.name ) );
						if ( locationCode != null && !locationCodeMap.containsKey( locationCode ) )
						{
							City city = new City();
							city.setCode( ontologyManager.getNamedIndividual( ontoTerm.getIri() ).getIRI().getFragment() );
							city.setReference( locationCode );
							city.setName( locationName );

							locationCodeMap.put( locationCode, city );
						}

					}
					else
					{
						classEx.add( ontologyManager.getOWLObjectHasValue( ontologyManager.getObjectProperty( isNearPlaceOf ), ontologyManager.getNamedIndividual( ontoTerm.getIri() ) ) );
					}
				}
			}

			if ( !classEx.isEmpty() )
			{
				allTermExpressions.add( ontologyManager.getOWLObjectUnionOf( classEx ) );
			}

			HopeLogger.log( searchTerm.toString() );
		}

		HopeLogger.log( "All term expression :" + allTermExpressions.toString() );
		HopeLogger.log( "----------- Get ontology terms completed -----------" );

		if ( allTermExpressions.size() == 0 )
		{
			HopeLogger.log( "No results are selected for the search query" );
			return results;
		}

		HopeLogger.log( "Final Search Query :" + ontologyManager.getOWLObjectIntersectionOf( allTermExpressions ) );
		Set<OWLNamedIndividual> individuals = ontologyManager.getIndividuals( ontologyManager.getOWLObjectIntersectionOf( allTermExpressions ) );
		for ( OWLNamedIndividual individual : individuals )
		{
			System.out.println( "Individual : " + individual );
			if ( isHotel( individual.getIRI().toString() ) )
			{
				// get the display name
				String displayName = ontologyManager.getAnnotationPropertyValueString( individual, ontologyManager.getOWLAnnotationProperty( OntologyManager.strApDisplayName ) );
				String locationCode = ontologyManager.getAnnotationPropertyValueString( individual, ontologyManager.getOWLAnnotationProperty( OntologyManager.locationCode ) );
				if ( locationCode != null && !locationCodeMap.containsKey( locationCode ) )
				{
					Set<OWLNamedIndividual> locations = ontologyManager.getIndividuals( ontologyManager.getOWLObjectHasValue( ontologyManager.getObjectProperty( isContainsPlace ), individual ) );
					if ( locations.size() > 0 )
					{
						OWLNamedIndividual owlNamedIndividual = locations.iterator().next();

						City city = new City();
						city.setCode( owlNamedIndividual.getIRI().getFragment() );
						city.setReference( locationCode );
						city.setName( locationCode );
						locationCodeMap.put( locationCode, city );
					}
				}

				Hotel hotel = new Hotel();
				hotel.setName( displayName );
				results.add( hotel );
			}
			else if ( isLocation( individual.getIRI().toString() ) )
			{
				String locationCode = ontologyManager.getAnnotationPropertyValueString( individual, ontologyManager.getOWLAnnotationProperty( OntologyManager.code ) );
				String locationName = ontologyManager.getAnnotationPropertyValueString( individual, ontologyManager.getOWLAnnotationProperty( OntologyManager.name ) );
				if ( locationCode != null && !locationCodeMap.containsKey( locationCode ) )
				{
					City city = new City();
					city.setCode( individual.getIRI().getFragment() );
					city.setReference( locationCode );
					city.setName( locationName );

					locationCodeMap.put( locationCode, city );
				}
			}
			else
			{
				// else we get the city that it is located
				Set<OWLNamedIndividual> parentCities = ontologyManager.getPropertyHolders( ontologyManager.getNamedIndividual( individual.getIRI().toString() ), ontologyManager.getObjectProperty( isInLocation ) );
				for ( OWLNamedIndividual parentCity : parentCities )
				{
					String locationCode = ontologyManager.getAnnotationPropertyValueString( parentCity, ontologyManager.getOWLAnnotationProperty( OntologyManager.code ) );
					String locationName = ontologyManager.getAnnotationPropertyValueString( parentCity, ontologyManager.getOWLAnnotationProperty( OntologyManager.name ) );
					if ( locationCode != null && !locationCodeMap.containsKey( locationCode ) )
					{
						City city = new City();
						city.setCode( individual.getIRI().getFragment() );
						city.setReference( locationCode );
						city.setName( locationName );

						locationCodeMap.put( locationCode, city );
					}
				}
			}
		}

		// add the locations
		if ( !locationCodeMap.isEmpty() )
		{
			results.addAll( locationCodeMap.values() );
		}

		// add the refine criteria
		if ( !criteriaMap.isEmpty() )
		{
			results.addAll( criteriaMap.values() );
		}

		HopeLogger.log( "$$$$$$$$$$$$$$$$$ Task Completed $$$$$$$$$$$$$$$$" );

		return results;
	}

	private static boolean isCity( OntoTerm ontoTerm )
	{
		String iri = ontoTerm.getIri();
		return ( iri.contains( "city" ) );
	}

	private static boolean isHotel( String iri )
	{
		return ( iri.contains( "hotel" ) );
	}

	private static boolean isTown( OntoTerm ontoTerm )
	{
		String iri = ontoTerm.getIri();
		return ( iri.contains( "town" ) );
	}

	private static boolean isDistrict( OntoTerm ontoTerm )
	{
		String iri = ontoTerm.getIri();
		return ( iri.contains( "district" ) );
	}

	private static boolean isRegion( OntoTerm ontoTerm )
	{
		String iri = ontoTerm.getIri();
		return ( iri.contains( "region" ) );
	}

	private static boolean isSuburb( OntoTerm ontoTerm )
	{
		String iri = ontoTerm.getIri();
		return ( iri.contains( "suburb" ) );
	}

	private static boolean isLocation( String iri )
	{
		return ( iri.contains( "town" ) || iri.contains( "city" ) || iri.contains( "district" ) || iri.contains( "suburb" ) || iri.contains( "district" ) || iri.contains( "region" ) );
	}

	private static boolean isLocation( OntoTerm ontoTerm )
	{
		String iri = ontoTerm.getIri();
		return ( iri.contains( "town" ) || iri.contains( "city" ) || iri.contains( "district" ) || iri.contains( "suburb" ) || iri.contains( "district" ) || iri.contains( "region" ) );
	}
}
